package edu.vt.ece.rt.benchmark.list;

import java.util.ArrayList;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import org.jgroups.annotations.LocalAddress;

import edu.vt.ece.rt.dstm.Context;
import edu.vt.ece.rt.dstm.ContextDelegator;
import edu.vt.ece.rt.dstm.QObject;
import edu.vt.ece.rt.dstm.TMProxy;
import edu.vt.ece.rt.quorum.ClustersManager;
import edu.vt.ece.rt.quorum.Resource;
import edu.vt.ece.rt.quorum.util.AbortException;
import edu.vt.ece.rt.quorum.util.Names;
import edu.vt.ece.rt.util.Logger.Logger;
import edu.vt.ece.rt.benchmark.BenchmarkThread;
import edu.vt.ece.rt.benchmark.bank.BankAccount;


public class List extends QObject {

	int val;
	String id;
	String nextId;

	public List() {

	}

	public List(String Id ,int v) {
		id = Id;
		val = v;
		nextId = null;
		Resource.putQObject(getId(), this);
	}

	public List(String Id ,int v, String next) {
		id = Id;
		val = v;
		nextId = next;
		//		TMProxy.getTMProxy().publishObject(this);
		Resource.putQObject(getId(), this);
	}

	void setNext(String Id) {
		nextId = Id;
	}

	String getNext() {
		return nextId;
	}

	int getVal() {
		return val;
	}

	void setVal(int v) {
		val = v;
	}

	@Override
	public QObject clone() {
		List l = new List();
		l.setId(getId());
		l.setVersion(getVersion());
		l.setNext(getNext());
		l.setVal(getVal());
		l.setCloneOwner(getCloneOwner());
		l.setNewObject(isNewObject());
		return l;
	}

	@Override
	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}


	public static Boolean find(int val, Context parentContext) {

		Boolean found = null;
		Long priority = System.currentTimeMillis();

		for (int i = 0; i < 0x7fffffff; i++) {

			((BenchmarkThread)Thread.currentThread()).collectStatsRoot(i);
			Context context = ContextDelegator.getNestedContext();
			context.setParent(parentContext);
			context.setDescription("find element in list");
			context.sanityNested();
			context.setPriority(priority);

			Logger.debug("----Started----- R Transaction "+context.getContextId()+" on "+ClustersManager.baseClusterId);
			try {

				ArrayList<List> pair = getNode(val, context);
				List curr = pair.get(1);				

				found = curr != null && curr.getVal() == val;

				if (TMProxy.getTMProxy().requestCommit(context.getContextId())) {
					context.unsetParent(parentContext);
					return found;
				}
			} catch (AbortException e) {
				e.touch();
			}
		}
		Logger.fetal("Unable to complete transaction in given number of tries");

		return found;
	}

	public static boolean addWork(int val, Context context) throws AbortException {
		ArrayList<List> pair = getNode(val, context);
		List curr = pair.get(1);
		List prev = pair.get(0);
		boolean add = false;

		/* skip case where first element for simplicity */
		if (prev == null || curr == null) {
			/* fall through */
		} else {
			/* get object for write */
			curr = (List) TMProxy.getTMProxy().write(curr.getId(), context.getContextId());
			prev = (List) TMProxy.getTMProxy().write(prev.getId(), context.getContextId());

			Context dummy = ContextDelegator.getNestedContext();
			String id = Long.toString(dummy.getContextId());
			List newList = new List();
			newList.setVal(val);
			newList.setNext(curr.getId());
			newList.setId(id);
			newList.setNewObject(true);
			context.addToCreateSet(newList);
			prev.setNext(newList.getId());
			add = true;
		}

		return add;
	}

	public static boolean add(int val, Context parentContext) throws AbortException {

		for (int i = 0; i < 0x7fffffff; i++) {

			((BenchmarkThread)Thread.currentThread()).collectStatsInner(i);

			Context context = ContextDelegator.getNestedContext();
			context.setParent(parentContext);
			context.setDescription("add element "+val+" in list");
			context.sanityNested();

			Logger.debug("----Started----- R Transaction "+context.getContextId()+" on "+ClustersManager.baseClusterId);

			try {

				boolean add = addWork(val, context);

				context.mergeParent();
				return add;

			} catch (AbortException e) {
				if (e.getTransaction().longValue() != context.getContextId().longValue()) {
					Logger.debug("Exception in parent transaction "+e.getTransaction());
					//					System.out.println("Exception in parent transaction "+e.getTransaction());
					throw e;
				} else {
					Logger.debug("Exception in current transaction "+e.getTransaction());
					//					System.out.println("Exception in current transaction "+e.getTransaction());					
				}

			}
		}
		Logger.fetal("Unable to complete transaction in given number of tries");

		return false;
	}

	public static boolean deleteWork(int val, Context context) throws AbortException {
		ArrayList<List> pair = getNode(val, context);
		List curr = pair.get(1);				
		List prev = pair.get(0);
		String newNext = null;
		boolean delete = false;

		/* skip case where first element for simplicity*/
		if (prev == null || curr == null) {
			/* fall through */
		} else if (curr.getVal() == val) {
			/* get object for write */

			curr = (List) TMProxy.getTMProxy().write(curr.getId(), context.getContextId());
			newNext = curr.getNext();
			prev = (List) TMProxy.getTMProxy().write(prev.getId(), context.getContextId());
			prev.setNext(newNext);
			delete = true;
		} else {
			/* fall through */
		}

		return delete;
	}

	public static Boolean delete(int val, Context parentContext) throws AbortException{


		for (int i = 0; i < 0x7fffffff; i++) {

			((BenchmarkThread)Thread.currentThread()).collectStatsInner(i);

			Context context = ContextDelegator.getNestedContext();
			context.setParent(parentContext);
			context.setDescription("delete element "+val +" in list");
			context.sanityNested();


			Logger.debug("----Started----- R Transaction "+context.getContextId()+" on "+ClustersManager.baseClusterId);
			try {
				//				getNode(val, context);
				boolean del = deleteWork(val, context);
				context.mergeParent();
				return del;

				//				List size = (List) TMProxy.getTMProxy().write("size", context.getContextId());
				//				size.setVal(size.getVal());
				//				Thread.sleep(10L);

				//				updateSize(context);


				//				if (TMProxy.getTMProxy().requestCommit(context.getContextId())) {
				//					context.unsetParent(parentContext);
				//					return true;
				//				}
			} catch (AbortException e) {
				if (e.getTransaction().longValue() != context.getContextId().longValue()) {
					Logger.debug("Exception in parent transaction "+e.getTransaction());
					//					System.out.println("Exception in parent transaction "+e.getTransaction());
					throw e;
				} else {
					Logger.debug("Exception in current transaction "+e.getTransaction());
					//					System.out.println("Exception in current transaction "+e.getTransaction());					
				}

			}
		}
		Logger.fetal("Unable to complete transaction in given number of tries");

		return false;
	}


	static ArrayList<List> getNode(int val, Context context) throws AbortException {
		String headId = "0";
		String currId = headId;
		List prev = null;
		List curr = null;
		do {
			prev = curr;
			curr = (List) TMProxy.getTMProxy().read(currId, context.getContextId());
			//			try {
			//				Thread.sleep(50L);
			//			} catch (InterruptedException e) {
			//				e.printStackTrace();
			//			}
		} while(curr.getVal() < val && (currId = curr.getNext()) != null);

		curr = (curr.getVal() >= val)? curr : null;

		ArrayList<List> arr =new ArrayList<List>();
		arr.add(0, prev);
		arr.add(1, curr);
		//		if (curr != null)
		////			System.out.println("Returning curr node as "+curr.getVal());
		//		else 
		//			System.out.println("Node not found");
		return arr;
	}

	public static void updateSize(Context parentContext) {


		for (int i = 0; i < 0x7fffffff; i++) {
			Logger.debug("abort of update global "+i);

			if (i > 0) {
				((BenchmarkThread)Thread.currentThread()).incrementAborts();
				((BenchmarkThread)Thread.currentThread()).incrementInnerAborts();
				((BenchmarkThread)Thread.currentThread()).incrementWriteAborts();

				//				if (i > 100) {
				//					System.out.println("I am stuck for "+ClustersManager.baseClusterId);
				//				}

				//				try {
				//
				//					Logger.debug("Sleeping for "+i*(ClustersManager.baseClusterId+1)*100L);
				//					if (ClustersManager.baseClusterId != 0 && i > 10) {
				//						Thread.sleep(5000L);
				//					} else {
				//						Thread.sleep(i*(ClustersManager.baseClusterId)*100L);
				//					}	
				//					
				//					Logger.debug("Waking up from sleep ["+ClustersManager.baseClusterId+"]");
				//				} catch (InterruptedException e) {
				//					// TODO Auto-generated catch block
				//					e.printStackTrace();
				//				}

			}


			Context context = ContextDelegator.getNestedContext();
			context.setParent(parentContext);
			context.setDescription("update size ");
			context.sanityNested();

			Logger.debug("-----Started ----- W Transaction "+context.getContextId()+" on "+ClustersManager.baseClusterId);
			try {
				List size = (List) TMProxy.getTMProxy().write("size", context.getContextId());
				size.setVal(size.getVal());

				if (TMProxy.getTMProxy().requestCommit(context.getContextId())) {
					context.unsetParent(parentContext);
					Resource.displayObjects(context);
					return;
				}
			} catch (Exception e) {			
				e.printStackTrace();
			}

		}
		Logger.fetal("Unable to complete transaction in given number of tries");
	}


	static int sum(Context parentContext) {

		int sum = 0;

		if (ClustersManager.baseClusterId != 0)
			return 0;

		String headId = "0";
		String currId = headId;
		List curr = null;
		do {
			curr = (List)Resource.getQObject(currId);
			sum= sum + curr.getVal();
			Logger.debug("Final val is "+curr.getVal());
		} while((currId = curr.getNext()) != null);


		return sum;
	}

	public static void closedNestedList(int localObjectsCount) {

		Context parentContext = null;
		Long priority = System.currentTimeMillis();
		Boolean delOp;
		boolean del = false, add = false;
		int delVal = 0;
		int addVal = 0;


		for (int i = 0; i < 0x7fffffff; i++) {

			((BenchmarkThread)Thread.currentThread()).collectStatsRoot(i);

			Context context = ContextDelegator.getNestedContext();
			context.setParent(parentContext);
			context.setDescription("starting root transaction");
			context.sanityNested();
			context.setPriority(priority);


			Logger.debug("----Started----- R Transaction "+context.getContextId()+" on "+ClustersManager.baseClusterId);
			try {

				delVal = 0;
				addVal = 0;

				for (int j = 0; j < Integer.getInteger(Names.calls);j++) {

					Random random = new Random();
					int val = random.nextInt(localObjectsCount);
//					val = j+2;
					delOp = true;

					if (random.nextInt(100) > 50) {
						delOp = false;
					}

					if (delOp) {
						Logger.debug("delete op with val " +val);
						del = delete(val, context);
//						del = deleteWork(val, context);
						if (del)
							delVal = delVal + val;
						
					} else {
						add = add(val, context);
//						add = addWork(val, context);
						Logger.debug("add op with val " +val);
						//			
						if (add)
							addVal = addVal + val;

					}
				}

				if (TMProxy.getTMProxy().requestCommit(context.getContextId())) {
					context.unsetParent(parentContext);
						Benchmark.delete = Benchmark.delete - delVal; 
						Benchmark.addition = Benchmark.addition + addVal; 						
					return;
				}

			}  catch (AbortException e) {
				e.touch();
			}
		}
	}

}